#region license
# This file is part of Grawlix.
# 
# Grawlix is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
# 
# Grawlix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public
# License along with Grawlix.  If not, see
# <http://www.gnu.org/licenses/>.
#endregion

"""
Primitive 2-dimensional vector structure.
It can represent position, dimension or offset.
"""

namespace Grawlix
import Grawlix.Meta
import System

struct Vector():
"""A pair of cartesian coordinates."""
	
	def constructor(x as double, y as double):
	"""
	Construct from the x and y coordinates.
	Param x: The x coordinate.
	Param y: The y coordinate.
	"""
		_x = x
		_y = y
	
	static def FromMagnitudeDirection([required(magnitude >= 0)]
	                                  magnitude as double,
	                                  direction as double):
	"""
	Construct from magnitude and direction representation.
	Param magnitude: The magnitude of the vector.
	Param direction: The angle of the vector, in radians.
	"""
		return Vector(magnitude * Math.Cos(direction),
		              magnitude * Math.Sin(direction))
	
	[Duplicate(Normalize, ApplyToSelf: true)]
	static def Normalize(lhs as Vector) as Vector:
	"""
	Normalize the vector so that its length is 1.
	Param lhs: The vector to apply to.
	Returns: The normalized vector.
	"""
		mag = lhs.Magnitude
		if mag == 0:
			return Vector(1, 0)
		return lhs / mag
	
	[Duplicate(DotProduct, ApplyToSelf: true)]
	static def DotProduct(lhs as Vector, rhs as Vector):
	"""
	Compute the dot product of two vectors.
	Param lhs: The multiplicand.
	Param rhs: The multiplier.
	Returns: The dot product of the two vectors.
	"""
		return lhs.X * rhs.X + lhs.Y * rhs.Y

	[Duplicate(Negate)]
	[Duplicate(Negate, ApplyToSelf: true)]
	static def op_UnaryNegation(vector as Vector):
	"""
	Vector negation.
	Param vector: The vector to negate.
	Returns: The negation of the vector.
	"""
		return Vector(-vector.X, -vector.Y)
	
	[Duplicate(Add)]
	[Duplicate(Add, ApplyToSelf: true)]
	static def op_Addition(lhs as Vector, rhs as Vector):
	"""
	Vector addition.
	Param rhs: One addend.
	Param lhs: The other addend.
	Returns: The sum of rhs and lhs.
	"""
		return Vector(lhs.X + rhs.X, lhs.Y + rhs.Y)
	
	[Duplicate(Subtract)]
	[Duplicate(Subtract, ApplyToSelf: true)]
	static def op_Subtraction(lhs as Vector, rhs as Vector):
	"""
	Vector subtraction.
	Param u: The subtrahend.
	Param v: The minuend.
	Returns: The difference between u and v.
	"""
		return op_Addition(lhs, -rhs)
	
	[Duplicate(Multiply)]
	[Duplicate(Multiply, ApplyToSelf: true)]
	static def op_Multiply(vector as Vector, scalar as double):
	"""
	Scalar multiplication.
	Param vector: The multiplicand.
	Param scalar: The multiplier.
	Returns: The product between vector and scalar.
	"""
		return Vector(vector.X * scalar, vector.Y * scalar)
	
	[Duplicate(Multiply)]
	static def op_Multiply(scalar as double, vector as Vector):
	"""
	Scalar multiplication.
	Param scalar: The multiplicand.
	Param vector: The multiplier.
	Returns: The product between scalar and vector.
	"""
		return op_Multiply(vector, scalar)
	
	[Duplicate(Divide)]
	[Duplicate(Divide, ApplyToSelf: true)]
	static def op_Division(vector as Vector, scalar as double):
	"""
	Scalar multiplication by reciprocal.
	Param vector: The multiplicand.
	Param scalar: The inverse of the multiplier.
	Returns: The product between vector and 1 / scalar.
	"""
		return Vector(vector.X / scalar, vector.Y / scalar)
	
	[Duplicate(Divide)]
	static def op_Division(scalar as double, vector as Vector):
	"""
	Scalar multiplication by reciprocal.
	Param scalar: The multiplicand.
	Param vector: The component-wise inverse of the multiplier.
	Returns: The product between scalar and 1 / vector.
	"""
		return Vector(scalar / vector.X, scalar / vector.Y)
	
	[Duplicate(Multiply)]
	[Duplicate(Multiply, ApplyToSelf: true)]
	static def op_Multiply(lhs as Vector, rhs as Vector):
	"""
	Vector multiplication, defined as lhs rotated and scaled by rhs.
	Param lhs: The multiplicand.
	Param rhs: The multiplier.
	Returns: The memberwise product of the vector.
	"""
		return Vector(lhs.X * rhs.X, lhs.Y * rhs.Y)
	
	[Equality(OverloadOperators: true)]
	private def Equality(other as Vector):
	"""
	Equality testing.
	Param other: The vector to compare to.
	Returns: true if equal, else false.
	"""
		return (X == other.X and Y == other.Y)
	
	override def GetHashCode():
	"""
	A hash code based on the current value.
	Returns: The hash code.
	"""
		return X.GetHashCode() ^ Y.GetHashCode()
	
	override def ToString():
	"""
	Get a string representation for this object.
	Returns: A string representation for this object.
	"""
		return "(${X}, ${Y})"
	
	Magnitude as double:
	"""The magnitude of the vector."""
		get:
			return Math.Sqrt(DotProduct(self))
		set:
			vector = self.Normalize() * value
			X = vector.X
			Y = vector.Y
	
	Direction as double:
	"""The direction of the vector, in radians."""
		get:
			return Math.Atan2(Y, X)
		set:
			vector = self * Vector(Math.Cos(value), Math.Sin(value))
			X = vector.X
			Y = vector.Y
	
	[property(X)]
	private _x as double
	"""The x coordinate of the vector."""
	
	[property(Y)]
	private _y as double
	"""The y coordinate of the vector."""
	
	